﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using ALinq.BBS.App_GlobalResources;
using System.Text;
using System.Net.Mail;
using System.Net.Sockets;
using System.Net.Configuration;
using System.Web.Compilation;
using System.Web.Configuration;
using ALinq.BBS.Common;

namespace ALinq.BBS
{
    public interface IEmail
    {
        string Subject { get; }
        string To { get; }
        string Body { get; }
        string From { get; }
    }

    public class RegisterEmail : IEmail
    {
        private readonly string body;
        private readonly string to;
        private readonly string subject;
        private readonly string from;

        public RegisterEmail(string mailTo, string returnUrl)
        {
            var path = "~/EmailTemplates/Register.cshtml";
            var type = BuildManager.GetCompiledType(path);
            var script = Activator.CreateInstance(type) as System.Web.WebPages.WebPage;

            var model = new { returnUrl };
            var writer = new System.IO.StringWriter();

            script.ExecutePageHierarchy(new System.Web.WebPages.WebPageContext(new HttpContextWrapper(HttpContext.Current),
                                            null, //rendering page
                                            model), //model
                                      writer);




            //var host = HttpContext.Current.Request.ServerVariables["HTTP_HOST"];
            //string href = "http://" + WebUtility.Host + "/";
            //var sb = new StringBuilder();
            //var writer = new StringWriter(sb);

            //var facotry = (PageHandlerFactory)Activator.CreateInstance(typeof(PageHandlerFactory), true);
            //var path = Path.Combine("~/EmailTemplate/Register.aspx");
            //var handler = facotry.GetHandler(HttpContext.Current, "Get", path, string.Empty);
            //var t = handler.GetType();

            //var m = t.GetProperty("UserName");
            //m.SetValue(handler, username, null);

            //m = t.GetProperty("RedirectUrl");
            //m.SetValue(handler, href, null);

            //m = t.GetProperty("Title");
            //m.SetValue(handler, "注册成功", null);

            //HttpContext.Current.Server.Execute(handler, writer, false);
            subject = Label.RegisterSuccess;
            var config = WebConfigurationManager.OpenWebConfiguration("~/Web.config");
            var smtpSection = (SmtpSection)config.GetSection("system.net/mailSettings/smtp");
            to = mailTo;
            from = string.Format("{1}<{0}>", smtpSection.From, ForumSection.Instance.ForumName);
            body = writer.ToString();
        }

        public string Subject
        {
            get { return subject; }
        }

        public string To
        {
            get { return to; }
        }

        public string Body
        {
            get { return body; }
        }

        public string From
        {
            get { return from; }
        }
    }

    public class Emailer
    {
        readonly TcpClient client = new TcpClient();
        NetworkStream stream;
        private readonly string host;
        private readonly int port;
        private readonly string userName;
        private readonly string password;
        Encoding encoding;
        private IEmail mail;
        private StringBuilder sbLog;

        private readonly SmtpSection smtpSection;

        public Emailer()
        {
            var config = WebConfigurationManager.OpenWebConfiguration("~/Web.config");
            smtpSection = (SmtpSection)config.GetSection("system.net/mailSettings/smtp");
            host = smtpSection.Network.Host;
            port = smtpSection.Network.Port;
            userName = smtpSection.Network.UserName;
            password = smtpSection.Network.Password;
        }

        private void Send(IEmail mail, Encoding encoding)
        {
            this.encoding = encoding;
            this.mail = mail;
            Connect();
            Auth();
            Send();
            Quit();
            Close();
        }

        public void Send(IEmail mail)
        {
            var config = WebConfigurationManager.OpenWebConfiguration("~/Web.config");
            GlobalizationSection configSection =
                    (GlobalizationSection)config.GetSection("system.web/globalization");
            Send(mail, configSection.ResponseEncoding);
        }

        void Send()
        {
            MailAddress[] addFrom = ParseAddress(mail.From);
            WriteData("MAIL FROM: <" + addFrom[0].Address + ">");
            ReadData();

            MailAddress[] addTo = ParseAddress(mail.To);
            foreach (MailAddress add in addTo)
            {
                WriteData("RCPT TO: <" + add.Address + ">");
                ReadData();
            }

            WriteData("DATA");
            ReadData();
            WriteData("From: " + EncodeAddress(addFrom[0]));
            WriteData("To: " + mail.To);
            WriteData("Subject: " + EncodeSubject(mail.Subject));
            WriteData("Date: " + DateTime.Now.ToString("R"));
            WriteData("MIME-Version: 1.0");
            WriteData("Content-Type: multipart/related; boundary=\"#SEPERATOR2#\"");
            WriteData("Content-Transfer-Encoding: quoted-printable\r\n");
            WriteData("--#SEPERATOR2#");
            WriteData(string.Format("Content-Type: text/html; charset={0}", encoding.HeaderName));
            WriteData("Content-Transfer-Encoding: quoted-printable\r\n");
            WriteData(EncodeBodyAsQuotedPrintable(mail.Body));
            WriteData("\r\n--#SEPERATOR2#--");
            WriteData(".");
            ReadData();

        }

        private void Connect()
        {
            client.Connect(host, port);
            stream = client.GetStream();
            ReadData();

            WriteData("EHLO Kolo.net.cn");
            ReadData();
        }

        private void Auth()
        {
            WriteData("AUTH LOGIN");
            ReadData();
            WriteData(Convert.ToBase64String(Encoding.ASCII.GetBytes(userName)));
            ReadData();
            WriteData(Convert.ToBase64String(Encoding.ASCII.GetBytes(password)));
            ReadData();
        }

        void Quit()
        {
            WriteData("QUIT ");
            ReadData();
        }

        private void Close()
        {
            stream.Close();
            client.Close();
        }

        void ReadData()
        {
            const int BUFFER_SIZE = 128;
            string response;
            byte[] buffer = new byte[BUFFER_SIZE];
            do
            {
                stream.Read(buffer, 0, buffer.Length);
                response = Encoding.ASCII.GetString(buffer).TrimEnd();
#if DEBUG
                Console.WriteLine(response);
#endif
                Log(response);
            }
            while (stream.DataAvailable);
            int code = Convert.ToInt32(response.Substring(0, 3));
            if (!CorrectedResponse(code))
                throw new Exception(response);
        }

        private static bool CorrectedResponse(int code)
        {
            if (code < 399)
                return true;
            else if (code > 399 & code < 599)
                return false;
            return false;
        }

        void WriteData(string text)
        {
            byte[] buffer = Encoding.ASCII.GetBytes(text + "\r\n");
            stream.Write(buffer, 0, buffer.Length);
#if DEBUG
            Console.WriteLine(text);
#endif
            Log(text);
        }

        /// <summary>
        /// Encode the body as in quoted-printable format.
        /// Adapted from PJ Naughter's quoted-printable encoding code.
        /// For more information see RFC 2045.
        /// </summary>
        /// <returns>The encoded body.</returns>
        private string EncodeBodyAsQuotedPrintable(string text)
        {
            StringBuilder buf = new StringBuilder();
            sbyte cur;
            byte[] bytes = encoding.GetBytes(text);
            for (int x = 0; x < bytes.Length; ++x)
            {
                cur = (sbyte)bytes[x];
                //is this a valid ascii character?
                if (((cur >= 33) && (cur <= 60)) || ((cur >= 62) && (cur <= 126)) || (cur == '\r') || (cur == '\n') || (cur == '\t') || (cur == ' '))
                {
                    //buf.Append(body[x]);
                    buf.Append(Convert.ToChar(bytes[x]));
                }
                else
                {
                    buf.Append('=');
                    buf.Append(((sbyte)((cur & 0xF0) >> 4)).ToString("X"));
                    buf.Append(((sbyte)(cur & 0x0F)).ToString("X"));
                }
            }
            //format data so that lines don't end with spaces (if so, add a trailing '='), etc.
            //for more detail see RFC 2045.

            int start = 0;
            string enc = buf.ToString();
            buf.Length = 0;
            for (int x = 0; x < enc.Length; ++x)
            {
                cur = (sbyte)enc[x];
                if (cur == '\n' || cur == '\r' || x == (enc.Length - 1))
                {
                    buf.Append(enc.Substring(start, x - start + 1));
                    start = x + 1;
                    continue;
                }
                if ((x - start) > 76)
                {
                    bool inWord = true;
                    while (inWord)
                    {
                        inWord = (!char.IsWhiteSpace(enc, x) && enc[x - 2] != '=');
                        if (inWord)
                        {
                            --x;
                            cur = (sbyte)enc[x];
                        }
                        if (x == start)
                        {
                            x = start + 76;
                            break;
                        }
                    }
                    buf.Append(enc.Substring(start, x - start + 1));
                    buf.Append("=\r\n");
                    start = x + 1;
                }
            }
            return buf.ToString();
        }

        private void Log(string msg)
        {
            if (sbLog == null)
                sbLog = new StringBuilder();

            sbLog.AppendFormat("{0}\r\n", msg);
        }

        MailAddress[] ParseAddress(string address)
        {
            string[] addArray = address.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
            MailAddress[] mailAddress = new MailAddress[addArray.Length];
            for (int i = 0; i < addArray.Length; i++)
            {
                //string add = addArray[i];
                int begin = addArray[i].IndexOf('<');
                int end = addArray[i].IndexOf('>');
                int len = end - begin - 1;
                if (begin < 0 & end < 0)
                {
                    mailAddress[i] = new MailAddress(addArray[i]);
                }
                else if (begin > 0 & end > 0 & len > 0)
                {
                    string add = addArray[i].Substring(begin + 1, len);
                    string name = addArray[i].Substring(0, begin);
                    mailAddress[i] = new MailAddress(add, name, encoding);
                }
                else
                {
                    throw new Exception("邮件格式错误！");
                }
            }
            return mailAddress;
        }

        string EncodeSubject(string subject)
        {
            byte[] buffer = encoding.GetBytes(subject);
            //Console.WriteLine(Convert.ToBase64String(buffer));
            return string.Format("=?{0}?B?{1}?=", encoding.BodyName,
                                 Convert.ToBase64String(buffer));
            //encoding.GetString(
        }

        string EncodeAddress(MailAddress address)
        {
            byte[] buffer = encoding.GetBytes(address.DisplayName);
            return string.Format("=?{0}?B?{1}?= <{2}>", encoding.BodyName,
                                 Convert.ToBase64String(buffer), address.Address);
        }



    }
}